//! Actor system implementation
//! 
//! This module implements the core actor system using coerce-rs,
//! providing the distributed execution engine for the test platform.

pub mod test_suite;
pub mod session;
pub mod step;
pub mod plugin;
pub mod ai_orchestration;
pub mod report;
pub mod messages;

// Re-export commonly used types
pub use test_suite::TestSuiteActor;
pub use session::SessionActor;
pub use step::StepActor;
pub use plugin::PluginActor;
pub use ai_orchestration::AIOrchestrationActor;
pub use report::ReportActor;
pub use messages::*;

use crate::error::Result;
use coerce::actor::system::ActorSystem;
use coerce::actor::ActorRef;
use coerce::actor::scheduler::ActorType;
use std::sync::Arc;
use uuid::Uuid;

/// Actor system manager
pub struct ActorSystemManager {
    /// The coerce actor system
    pub system: ActorSystem,
    
    /// Test suite actor reference
    pub test_suite_actor: Option<ActorRef<TestSuiteActor>>,
    
    /// Plugin actor reference
    pub plugin_actor: Option<ActorRef<PluginActor>>,
    
    /// AI orchestration actor reference
    pub ai_actor: Option<ActorRef<AIOrchestrationActor>>,
    
    /// Report actor reference
    pub report_actor: Option<ActorRef<ReportActor>>,
}

impl ActorSystemManager {
    /// Create a new actor system manager
    pub async fn new() -> Result<Self> {
        let system = ActorSystem::new();
        
        Ok(Self {
            system,
            test_suite_actor: None,
            plugin_actor: None,
            ai_actor: None,
            report_actor: None,
        })
    }
    
    /// Initialize all core actors
    pub async fn initialize(&mut self) -> Result<()> {
        // Create plugin actor first (other actors may depend on it)
        let plugin_actor = self.system
            .new_actor("plugin-actor".to_string(), PluginActor::new(), ActorType::Tracked)
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create plugin actor: {}", e)))?;
        self.plugin_actor = Some(plugin_actor.clone().into());

        // Create AI orchestration actor
        let ai_actor = self.system
            .new_actor("ai-orchestration-actor".to_string(), AIOrchestrationActor::new(), ActorType::Tracked)
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create AI actor: {}", e)))?;
        self.ai_actor = Some(ai_actor.clone().into());

        // Create report actor
        let report_actor = self.system
            .new_actor("report-actor".to_string(), ReportActor::new(), ActorType::Tracked)
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create report actor: {}", e)))?;
        self.report_actor = Some(report_actor.clone().into());

        // Create test suite actor
        let test_suite_actor = self.system
            .new_actor(
                "test-suite-actor".to_string(),
                TestSuiteActor::new(plugin_actor.into(), ai_actor.into(), report_actor.into()),
                ActorType::Tracked,
            )
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create test suite actor: {}", e)))?;
        self.test_suite_actor = Some(test_suite_actor.into());
        
        tracing::info!("Actor system initialized successfully");
        Ok(())
    }
    
    /// Get test suite actor reference
    pub fn test_suite_actor(&self) -> Option<&ActorRef<TestSuiteActor>> {
        self.test_suite_actor.as_ref()
    }
    
    /// Get plugin actor reference
    pub fn plugin_actor(&self) -> Option<&ActorRef<PluginActor>> {
        self.plugin_actor.as_ref()
    }
    
    /// Get AI actor reference
    pub fn ai_actor(&self) -> Option<&ActorRef<AIOrchestrationActor>> {
        self.ai_actor.as_ref()
    }
    
    /// Get report actor reference
    pub fn report_actor(&self) -> Option<&ActorRef<ReportActor>> {
        self.report_actor.as_ref()
    }
    
    /// Create a new session actor
    pub async fn create_session_actor(&self, session_id: Uuid) -> Result<ActorRef<SessionActor>> {
        let actor_name = format!("session-actor-{}", session_id);
        
        let session_actor = self.system
            .new_actor(
                actor_name,
                SessionActor::new(
                    session_id,
                    self.plugin_actor.clone(),
                    self.ai_actor.clone(),
                    self.report_actor.clone(),
                ),
                ActorType::Tracked,
            )
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create session actor: {}", e)))?;

        Ok(session_actor.into())
    }
    
    /// Create a new step actor
    pub async fn create_step_actor(&self, step_id: Uuid) -> Result<ActorRef<StepActor>> {
        let actor_name = format!("step-actor-{}", step_id);
        
        let step_actor = self.system
            .new_actor(
                actor_name,
                StepActor::new(
                    step_id,
                    self.plugin_actor.clone(),
                    self.ai_actor.clone(),
                ),
                ActorType::Tracked,
            )
            .await
            .map_err(|e| crate::error::internal_error(format!("Failed to create step actor: {}", e)))?;

        Ok(step_actor.into())
    }
    
    /// Shutdown the actor system
    pub async fn shutdown(&mut self) -> Result<()> {
        tracing::info!("Shutting down actor system");

        // Note: In coerce-rs, actors are automatically stopped when the system shuts down
        // We don't need to manually stop individual actors

        // Shutdown the actor system
        self.system.shutdown().await;

        tracing::info!("Actor system shutdown completed");
        Ok(())
    }
}

/// Global actor system instance
static mut ACTOR_SYSTEM: Option<Arc<tokio::sync::RwLock<ActorSystemManager>>> = None;
static INIT: std::sync::Once = std::sync::Once::new();

/// Initialize the global actor system
pub async fn init_actor_system() -> Result<()> {
    INIT.call_once(|| {
        let rt = tokio::runtime::Handle::current();
        rt.spawn(async {
            let manager = ActorSystemManager::new().await.expect("Failed to create actor system");
            unsafe {
                ACTOR_SYSTEM = Some(Arc::new(tokio::sync::RwLock::new(manager)));
            }
        });
    });
    
    // Wait for initialization to complete
    while unsafe { ACTOR_SYSTEM.is_none() } {
        tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
    }
    
    // Initialize actors
    let system = get_actor_system().await?;
    let mut manager = system.write().await;
    manager.initialize().await?;
    
    Ok(())
}

/// Get the global actor system
pub async fn get_actor_system() -> Result<Arc<tokio::sync::RwLock<ActorSystemManager>>> {
    unsafe {
        ACTOR_SYSTEM
            .as_ref()
            .cloned()
            .ok_or_else(|| crate::error::internal_error("Actor system not initialized"))
    }
}

/// Shutdown the global actor system
pub async fn shutdown_actor_system() -> Result<()> {
    if let Ok(system) = get_actor_system().await {
        let mut manager = system.write().await;
        manager.shutdown().await?;
    }
    
    unsafe {
        ACTOR_SYSTEM = None;
    }
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_actor_system_creation() {
        let manager = ActorSystemManager::new().await.unwrap();
        assert!(manager.test_suite_actor.is_none());
        assert!(manager.plugin_actor.is_none());
    }

    #[tokio::test]
    async fn test_actor_system_initialization() {
        let mut manager = ActorSystemManager::new().await.unwrap();
        manager.initialize().await.unwrap();
        
        assert!(manager.test_suite_actor.is_some());
        assert!(manager.plugin_actor.is_some());
        assert!(manager.ai_actor.is_some());
        assert!(manager.report_actor.is_some());
        
        manager.shutdown().await.unwrap();
    }
}
